[Chapter Ten][Previous]
[Art of Assembly][Randall
Hyde]
Art of Assembly: Chapter Ten
- 10.11 - Sample Program
10.11 Sample Program
This chapter's sample program is a simple moon lander game. While the
simulation isn't terribly realistic, this program does demonstrate the use
and optimization of several different control structures including loops,
if..then..else statements, and so on.
; Simple "Moon Lander" game.
;
; Randall Hyde
; 2/8/96
;
; This program is an example of a trivial little "moon lander"
; game that simulates a Lunar Module setting down on the Moon's
; surface. At time T=0 the spacecraft's velocity is 1000 ft/sec
; downward, the craft has 1000 units of fuel, and the craft is
; 10,000 ft above the moon's surface. The pilot (user) can
; specify how much fuel to burn at each second.
;
; Note that all calculations are approximate since everything is
; done with integer arithmetic.
; Some important constants
InitialVelocity = 1000
InitialDistance = 10000
InitialFuel = 250
MaxFuelBurn = 25
MoonsGravity = 5 ;Approx 5 ft/sec/sec
AccPerUnitFuel = -5 ;-5 ft/sec/sec for each fuel unit.
.xlist
include stdlib.a
includelib stdlib.lib
.list
.386 ;Comment out these two statements
option segment:use16 ; if you are not using an 80386.
dseg segment para public 'data'
; Current distance from the Moon's Surface:
CurDist word InitialDistance
; Current Velocity:
CurVel word InitialVelocity
; Total fuel left to burn:
FuelLeft word InitialFuel
; Amount of Fuel to use on current burn.
Fuel word ?
; Distance travelled in the last second.
Dist word ?
dseg ends
cseg segment para public 'code'
assume cs:cseg, ds:dseg
; GETI- Reads an integer variable from the user and returns its
; its value in the AX register. If the user entered garbage,
; this code will make the user re-enter the value.
geti textequ <call _geti>
_geti proc
push es
push di
push bx
; Read a string of characters from the user.
;
; Note that there are two (nested) loops here. The outer loop
; (GetILp) repeats the getsm operation as long as the user
; keeps entering an invalid number. The innermost loop (ChkDigits)
; checks the individual characters in the input string to make
; sure they are all decimal digits.
GetILp: getsm
; Check to see if this string contains any non-digit characters:
;
; while (([bx] >= '0') and ([bx] <= '9') bx := bx + 1;
;
; Note the sneaky way of turning the while loop into a
; repeat..until loop.
mov bx, di ;Pointer to start of string.
dec bx
ChkDigits: inc bx
mov al, es:[bx] ;Fetch next character.
IsDigit ;See if it's a decimal digit.
je ChkDigits ;Repeat if it is.
cmp al, 0 ;At end of string?
je GotNumber
; Okay, we just ran into a non-digit character. Complain and make
; the user reenter the value.
free ;Free space malloc'd by getsm.
print
byte cr,lf
byte "Illegal unsigned integer value, "
byte "please reenter.",cr,lf
byte "(no spaces, non-digit chars, etc.):",0
jmp GetILp
; Okay, ES:DI is pointing at something resembling a number. Convert
; it to an integer.
GotNumber: atoi
free ;Free space malloc'd by getsm.
pop bx
pop di
pop es
ret
_geti endp
; InitGame- Initializes global variables this game uses.
InitGame proc
mov CurVel, InitialVelocity
mov CurDist, InitialDistance
mov FuelLeft, InitialFuel
mov Dist, 0
ret
InitGame endp
; DispStatus- Displays important information for each
; cycle of the game (a cycle is one second).
DispStatus proc
printf
byte cr,lf
byte "Distance from surface: %5d",cr,lf
byte "Current velocity: %5d",cr,lf
byte "Fuel left: %5d",cr,lf
byte lf
byte "Dist travelled in the last second: %d",cr,lf
byte lf,0
dword CurDist, CurVel, FuelLeft, Dist
ret
DispStatus endp
; GetFuel- Reads an integer value representing the amount of fuel
; to burn from the user and checks to see if this value
; is reasonable. A reasonable value must:
;
; * Be an actual number (GETI handles this).
; * Be greater than or equal to zero (no burning
; negative amounts of fuel, GETI handles this).
; * Be less than MaxFuelBurn (any more than this and
; you have an explosion, not a burn).
; * Be less than the fuel left in the Lunar Module.
GetFuel proc
push ax
; Loop..endloop structure that reads an integer input and terminates
; if the input is reasonable. It prints a message an repeats if
; the input is not reasonable.
;
; loop
; get fuel;
; if (fuel < MaxFuelBurn) then break;
; print error message.
; endloop
;
; if (fuel > FuelLeft) then
;
; fuel = fuelleft;
; print appropriate message.
;
; endif
GetFuelLp: print
byte "Enter amount of fuel to burn: ",0
geti
cmp ax, MaxFuelBurn
jbe GoodFuel
print
byte "The amount you've specified exceeds the "
byte "engine rating,", cr, lf
byte "please enter a smaller value",cr,lf,lf,0
jmp GetFuelLp
GoodFuel: mov Fuel, ax
cmp ax, FuelLeft
jbe HasEnough
printf
byte "There are only %d units of fuel left.",cr,lf
byte "The Lunar module will burn this rather than %d"
byte cr,lf,0
dword FuelLeft, Fuel
mov ax, FuelLeft
mov Fuel, ax
HasEnough: mov ax, FuelLeft
sub ax, Fuel
mov FuelLeft, ax
pop ax
ret
GetFuel endp
; ComputeStatus-
;
; This routine computes the new velocity and new distance based on the
; current distance, current velocity, fuel burnt, and the moon's
; gravity. This routine is called for every "second" of flight time.
;
; note:
;
; Distance Travelled = Acc*T*T/2 + Vel*T (note: T=1, so it goes away).
; Acc = MoonsGravity + Fuel * AccPerUnitFuel
;
; New Velocity = Acc*T + Prev Velocity
;
; This code should really average these values over the one second
; time period, but the simulation is so crude anyway, there's no
; need to really bother.
ComputeStatus proc
push ax
push bx
push dx
; First, compute the acceleration value based on the fuel burnt
; during this second (Acc = Moon's Gravity + Fuel * AccPerUnitFuel).
mov ax, Fuel ;Compute
mov dx, AccPerUnitFuel ; Fuel*AccPerUnitFuel
imul dx
add ax, MoonsGravity ;Add in Moon's gravity.
mov bx, ax ;Save Acc value.
; Now compute the new velocity (V=AT+V)
add ax, CurVel ;Compute new velocity
mov CurVel, ax
; Next, compute the distance travelled (D = 1/2 * A * T^2 + VT +D)
sar bx, 1 ;Acc/2
add ax, bx ;Acc/2 + V (T=1!)
mov Dist, ax ;Distance Travelled.
neg ax
add CurDist, ax ;New distance.
pop dx
pop bx
pop ax
ret
ComputeStatus endp
; GetYorN- Reads a yes or no answer from the user (Y, y, N, or n).
; Returns the character read in the al register (Y or N,
; converted to upper case if necessary).
GetYorN proc
getc
ToUpper
cmp al, 'Y'
je GotIt
cmp al, 'N'
jne GetYorN
GotIt: ret
GetYorN endp
Main proc
mov ax, dseg
mov ds, ax
mov es, ax
meminit
MoonLoop: print
byte cr,lf,lf
byte "Welcome to the moon lander game.",cr,lf,lf
byte "You must manuever your craft so that you touch"
byte "down at less than 10 ft/sec",cr,lf
byte "for a soft landing.",cr,lf,lf,0
call InitGame
; The following loop repeats while the distance to the surface is greater
; than zero.
WhileStillUp: mov ax, CurDist
cmp ax, 0
jle Landed
call DispStatus
call GetFuel
call ComputeStatus
jmp WhileStillUp
Landed: cmp CurVel, 10
jle SoftLanding
printf
byte "Your current velocity is %d.",cr,lf
byte "That was just a little too fast. However, as a "
byte "consolation prize,",cr,lf
byte "we will name the new crater you just created "
byte "after you.",cr,lf,0
dword CurVel
jmp TryAgain
SoftLanding: printf
byte "Congrats! You landed the Lunar Module safely at "
byte "%d ft/sec.",cr,lf
byte "You have %d units of fuel left.",cr,lf
byte "Good job!",cr,lf,0
dword CurVel, FuelLeft
TryAgain: print
byte "Do you want to try again (Y/N)? ",0
call GetYorN
cmp al, 'Y'
je MoonLoop
print
byte cr,lf
byte "Thanks for playing! Come back to the moon again sometime"
byte cr,lf,lf,0
Quit: ExitPgm ;DOS macro to quit program.
Main endp
cseg ends
sseg segment para stack 'stack'
stk byte 1024 dup ("stack ")
sseg ends
zzzzzzseg segment para public 'zzzzzz'
LastBytes byte 16 dup (?)
zzzzzzseg ends
end Main
- 10.11 - Sample Program
Art of Assembly: Chapter Ten - 27 SEP 1996
[Chapter Ten][Previous]
[Art of Assembly][Randall
Hyde]